package com.zj.service;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zj.dao.BannerMapper;
import com.zj.model.Banner;
import com.zj.restApi.FileUploadApi;
import com.zj.restApi.IdGenratorApi;
import com.zj.web.bean.BannerDTO;
import com.zj.web.bean.ResponseResult;
import lombok.extern.java.Log;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Log
public class BannerServiceImpl extends ServiceImpl<BannerMapper, Banner> implements BannerService {
    @Autowired
    private RestTemplate restTemplate;   //情况一：product没有注册到nacos里面，所以不能用服务名调用
    @Autowired
    private BannerMapper bannerMapper;

//    @Override
//    public BannerDTO addBanner(BannerDTO bannerDTO) {
//        //1.调用 idGenerator 服务 取出id号
//        //情况一的RestTemplate测试,注意此处地址   why？ 因为 当前product并没有在nacos上注册，所以只能以ip:port访问
////        String url="http://localhost:8285/id/next";
////        ResponseResult responseResult = restTemplate.getForObject(url, ResponseResult.class);
////        log.info(   responseResult.toString() );
////        if(   responseResult==null ||  responseResult.getCode()!=1  ){
////            throw new RuntimeException( "调用idGenerator服务失败" );
////        }
////        Long id = (Long) responseResult.getData();
////        bannerDTO.setBannerId( id );
//
//
//        //两个服务使用不同的负载均衡策略
//        //情况二的RestTemplate测试,注意此处地址   why？ 因为 当前product已经注册到nacos上，所以可以以服务名调用
//        String url="http://idGenrator/id/next";
//        ResponseResult responseResult = restTemplate.getForObject(url, ResponseResult.class);
//        log.info(   responseResult.toString() );
//        if(   responseResult==null ||  responseResult.getCode()!=1  ){
//            throw new RuntimeException( "调用idGenerator服务失败" );
//        }
//        Long id = (Long) responseResult.getData();
//        bannerDTO.setBannerId( id );
//
//
//        //2.调用fileUpload服务   上传图片
//        String uploadUrl="http://fileUpload/upload";
////        String uploadUrl="http://localhost:12000/upload";
//        //涉及文件上传，需要构建请求体
//        //构建请求体
//        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
//        try {
//            // 这是哦那个界面上传过来的一个流，应当用流的方式来上传
//            ByteArrayResource byteArrayResource = new ByteArrayResource(bannerDTO.getImageFile().getBytes()) {
//                @Override
//                public String getFilename() {
//                    return bannerDTO.getImageFile().getOriginalFilename();
//                }
//            };
//            body.add("file",byteArrayResource);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//        //构建请求头
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//        //组装  构建请求实体
//        HttpEntity<MultiValueMap<String,Object>> requestEntity=new HttpEntity<>(body,headers);
//        ResponseResult responseResult1=restTemplate.postForObject(uploadUrl,requestEntity,ResponseResult.class);
//        log.info(   responseResult1.toString() );
//        if(   responseResult1==null ||  responseResult1.getCode()!=1  ){
//            throw new RuntimeException( "调用fileUpload服务失败" );
//        }
//       List<String> urls=(List<String>) responseResult1.getData();
//        bannerDTO.setImageUrl( urls.get(0) );
//        log.info("banner的图片地址为"+bannerDTO.getImageUrl());
////        bannerMapper.insert(bannerDTO);
//        return bannerDTO;
//    }

    //方案三：利用Feign客户端
    @Autowired
    private IdGenratorApi idGenratorApi;
    @Autowired
    private FileUploadApi fileUploadApi;
    @Override
    @Transactional
    //当新增货修改了轮播图时，需要清楚所有相关的缓存
    //“banner” :是缓存的名称
    //allEntries = true : 表示清除所有的缓存
    @CacheEvict(value = "banners",allEntries = true) //添加缓存清除
    //只要添加了轮播图，就需要清除缓存
    public BannerDTO addBanner(BannerDTO bannerDTO) {
        //1.调用 idGenerator 服务 取出id号
        ResponseEntity<ResponseResult<Long>> responseEntity = idGenratorApi.getNextId();  //jdk动态代理对象
        if(responseEntity.getStatusCode()!= HttpStatus.OK || responseEntity.getBody()==null){
            throw new RuntimeException("调用idGenerator服务失败");
        }
        ResponseResult<Long> responseResult = responseEntity.getBody();
        log.info(responseResult.toString());
        if (responseResult == null || responseResult.getCode() != 1) {
            throw new RuntimeException("调用idGenerator服务失败");
        }
        Long id = responseResult.getData();
        bannerDTO.setBannerId(id);

//        文件上传  利用Feign客户端
        ResponseEntity<ResponseResult> responseEntity1 = this.fileUploadApi.uploadFile(new MultipartFile[]{bannerDTO.getImageFile()});
        if(responseEntity1.getStatusCode()!= HttpStatus.OK || responseEntity1.getBody()==null){
            throw new RuntimeException("调用fileUpload服务失败");
        }
        ResponseResult responseResult1 = responseEntity1.getBody();
        log.info(responseResult1.toString());
        if (responseResult1 == null || responseResult1.getCode() != 1) {
            throw new RuntimeException("调用fileUpload服务失败");
        }
        List<String> urls=(List<String>) responseResult1.getData();
        bannerDTO.setImageUrl( urls.get(0) );

        // 3. 将 BannerDTO 转换为 Banner 实体对象
        Banner banner = new Banner();
        banner.setBannerStatus(BANNER_STATUS_NORMAL);
        // 使用 Spring 的 BeanUtils 拷贝同名属性
        // 这会将 bannerDTO 中与 banner 属性名相同且类型兼容的值拷贝过去
        //                      bannerDTO   banner Model
        BeanUtils.copyProperties(bannerDTO, banner);

        // 特别注意：如果有一些特殊字段需要手动处理，在这里设置
        // 例如，Banner 实体里没有 imageFile 字段，但 BannerDTO 有。
        // BeanUtils.copyProperties 会忽略 banner 中不存在的字段，所以是安全的。
        // banner.setSomeField(bannerDTO.getSomeSpecialField());

        // 4. 执行数据库插入操作，传入的是 Banner 实体对象
        Boolean flag = this.save(banner);
        if(!flag){
            throw new RuntimeException("添加失败");
        }

        return bannerDTO;
    }

    /**
     * 根据banner状态查询banner列表
     * @return
     */
    //根据状态信息查询轮播图
    @Override
    //启用缓存：
    //value="banners" : 缓存的名称，所有轮播图相关的缓存都存放在这个命名空间下
    //key="#bannerStatus!=null ? 'status_'+#bannerStatus : 'all'" : 缓存的key，这里使用bannerStatus作为key，因为根据状态查询轮播图是一个固定的操作，所以可以缓存
    //  - 如果bannerStatus为null，缓存的key为"all"，表示查询所有轮播图
    //  - 如果bannerStatus不为null，缓存的key为"status_"+bannerStatus，例如status_1表示查询状态为1的轮播图
    //这个策略可以区分不同状态的轮播图列表，同时也有一个缓存所有轮播图的入口，例如status_1和status_2的轮播图是不同的，所以可以缓存
    //key使用了spring el表达式，根据bannerStatus是否为null来动态生成key
    //condition="#bannerStatus != null" : 缓存的条件，只有当bannerStatus不为null时才缓存
    @Cacheable(value = "banners", keyGenerator = "BannerKeyGenerator")
    public List<BannerDTO> getBannersByStatus(Integer bannerStatus) {
        LambdaQueryWrapper<Banner> queryWrapper = new LambdaQueryWrapper<>();
        if (bannerStatus!=null && bannerStatus!=-1){
            queryWrapper.eq(Banner::getBannerStatus, bannerStatus);
        }
        queryWrapper.orderByDesc(Banner::getBannerId);
        List<Banner> banners = bannerMapper.selectList(queryWrapper);
        //类型转换 Banner Model 转成 Banner DTO
        return banners.stream().map(banner -> {
            BannerDTO bannerDTO = new BannerDTO();
            BeanUtils.copyProperties(banner, bannerDTO);
            return bannerDTO;
        }).collect(Collectors.toList());
    }

    @Override
    @CacheEvict(value = "banners",allEntries = true) //添加缓存清除
    public BannerDTO updateBannerStatus(Long bannerId, Integer bannerStatus) {
        //判断banner是否存在
        Banner banner = bannerMapper.selectById(bannerId);
        if(banner==null){
            throw new RuntimeException("banner不存在");
        }
        banner.setBannerStatus(bannerStatus);
        bannerMapper.updateById(banner);
        BannerDTO bannerDTO = new BannerDTO();
        BeanUtils.copyProperties(banner, bannerDTO);
        return bannerDTO;
    }
}
